#!/usr/bin/python
# -*- coding: UTF-8 -*-
import pymongo
import mongo_proxy
from web3 import Web3
from decimal import Decimal
from bson.decimal128 import Decimal128
import os
import time
from utils import convert_decimal, convert_decimal_for_dict
import utils
from datetime import datetime
import traceback
import constant
# w3 = Web3(Web3.WebsocketProvider("ws://112.31.76.10:28555"))
# w3 = Web3(Web3.WebsocketProvider("wss://mainnet.infura.io/ws/v3/6e6a3c3e676b4ab1ad7a7126b70169e9"))

# transfer_event = "Transfer(address,address,uint256)"
# transfer_event_hashed = w3.keccak(text=transfer_event)

client = mongo_proxy.MongoProxy(pymongo.MongoClient(os.getenv('db')))
db = client.eth
addresses = []
ignore_tokens = ['0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F',
                 '0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b']

last_report_data = list()


@utils.exception_handler
def get_address():
    global addresses
    print("try to sync addresses")
    addresses = list(db.addresses.distinct("address"))


def find_in_list(match, arr):
    for x in arr:
        if x["_id"] == match:
            return x
    return None


@utils.exception_handler
def send_diff():
    global last_report_data
    print("try to report")
    token_diff = list()
    last2 = list(db.last2.find({}).sort("count", pymongo.DESCENDING))
    token_in_lasst2 = [x['_id'] for x in last2]
    if len(last_report_data) != 0:
        token_in_last_report = [x['_id'] for x in last_report_data]
        token_diff = list(set(token_in_lasst2) - set(token_in_last_report))
    else:
        token_diff = list()
    # last10 = list(db.last10.find({}).sort("count",pymongo.DESCENDING).limit(100))
    # token_in_lasst10=[x['_id'] for x in last10]
    from datetime import datetime
    now = datetime.now()
    print("send_diff", now.strftime("%m/%d/%Y, %H:%M:%S"),
          len(token_diff), len(last2), len(last_report_data))
    last_report_data = last2
    if len(token_diff) < 1:
        return
    _html = []
    token_diff = token_diff[:20]

    for x in token_diff:
        _x = find_in_list(x, last2)
        if _x["count"] > 10:
            _html.append(
                # f'{_x["symbol"]}-<a target="_blank" href="https://etherscan.io/address/{_x["_id"]}">{_x["_id"]}</a>-{_x["count"]}')
                f'{_x["symbol"]}-{_x["_id"]}-{_x["count"]}')
    print("send_diff", now.strftime("%m/%d/%Y, %H:%M:%S"), _html)
    if len(_html):
        # utils.send_mail('<br/>'.join(_html), now.strftime("%m/%d/%Y, %H:%M:%S"), constant.emails)
        utils.send_mail('<br/>'.join(_html), now.strftime("%m/%d/%Y, %H:%M:%S"), constant.emails2)


def get_token_from_hub(to_account, receipt):
    token_address = to_account
    # opensea
    if to_account == '0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b':
        token_address = receipt.logs[0].address
    # sushi swap
    elif to_account == '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F':
        token_address = receipt.logs[2].address
    return token_address


def get_token_symbol(address, w3):
    symbol = None
    result = db.symbol.find_one({"address": address})
    if result == None:
        try:
            token_info = w3.eth.contract(
                w3.toChecksumAddress(address), abi=utils.ERC20_ABI)
            symbol = token_info.functions.symbol().call()
        except:
            symbol = "Unknown"
            pass
        db.symbol.insert_one({'address': address, "symbol": symbol})
    else:
        symbol = result['symbol']
    return symbol


def get_height_from_db(eth_block_num):
    result = db.investment.find_one(sort=[("block", -1)])
    if result == None:
        return eth_block_num-500
    return result['block']


def is_contract(address: str, w3):
    return w3.eth.getCode(address).hex() != '0x'


def start_listen():
    w3 = Web3(Web3.WebsocketProvider("ws://127.0.0.1:8556"))
    get_address()

    while True:
        time.sleep(20)
        eth_block_num = w3.eth.block_number
        start_block = get_height_from_db(eth_block_num)
        if start_block == eth_block_num:
            time.sleep(3)
            continue
        end_block = eth_block_num
        for block_num in range(start_block, end_block):
            print("block_num", block_num)
            try:
                block = w3.eth.getBlock(block_num, full_transactions=True)
                list_of_block_transactions = block.transactions
                for transaction in list_of_block_transactions:
                    to_account = transaction['to']
                    from_account = transaction['from']
                    if from_account in addresses and to_account not in ignore_tokens:
                        try:
                            receipt = w3.eth.get_transaction_receipt(
                                transaction['hash'])
                            if receipt.status == 1:
                                # token_address=get_token_from_hub(to_account,receipt)
                                object = {
                                    "block": transaction["blockNumber"],
                                    "address": from_account,
                                    "to": to_account,
                                    "symbol": get_token_symbol(to_account, w3) if transaction['input'] != '0x' else "ETH",
                                    "input": transaction['input'],
                                    "value": Web3.fromWei(transaction['value'], 'ether'),
                                    "hash": transaction['hash'].hex(),
                                    "type": "contract" if is_contract(transaction['to'],w3) else "transfer"
                                }
                                #  or transaction['type']=="0x2"
                                object = convert_decimal_for_dict(object)
                                db.investment.insert_one(object)
                        except Exception as err:
                            traceback.print_exc()
                            pass
            except Exception as errN:
                pass


utils.set_interval(get_address, 60)
utils.set_interval(send_diff, 60*20)